#ifndef CONSTRAINT_PARSER_H
#define CONSTRAINT_PARSER_H
#include <iostream>
#include <vector>
#include <map>
#include <llvm/GlobalVariable.h>
#include <llvm/Value.h>
#include <llvm/Function.h>
#include <llvm/Module.h>
#include <llvm/Instructions.h>
#include <llvm/Support/InstIterator.h>
#include <llvm/Support/raw_ostream.h>

namespace mypointsto{

	enum cons_t {base_t, simple_t, load_t, store_t};

	struct constraint{
		cons_t type;
	};

	struct base_constraint : constraint{
		int ptr_id, val_id;
		base_constraint(int p, int v){
			type = base_t;
			ptr_id = p;
			val_id = v;
		}
	};

	struct simple_constraint : constraint{
		int src_id, dst_id;
		simple_constraint(int s, int d){
			type = simple_t;
			src_id = s;
			dst_id = d;
		}
	};

	struct load_constraint : constraint{
		int addr_id, val_id;
		load_constraint(int a, int v){
			type = load_t;
			addr_id = a;
			val_id = v;
		}
	};

	struct store_constraint : constraint{
		int val_id, addr_id;
		store_constraint(int v, int a){
			type = store_t;
			val_id = v;
			addr_id = a;
		}
	};

	enum label_t {global_t, local_t, heap_t, deref_t};

	struct label{
		label_t type;
		virtual int label_cmp(label *rhs) = 0;
	};

	struct global_label : label{
		llvm::GlobalVariable *global_v;
		global_label(llvm::GlobalVariable *gv){
			global_v = gv;
			type = global_t;
		}
		~global_label(){
			if(global_v)delete global_v;
		}
		int label_cmp(label *rhs);
	};

	struct local_label : label{
		llvm::Function *func;
		llvm::Value *value;
		local_label(llvm::Function *f, llvm::Value *v){
			func = f;
			value = v;
			type = local_t;
		}
		~local_label(){
			if(func)delete func;
			if(value)delete value;
		}
		int label_cmp(label *rhs);
	};

	struct heap_label : label{
		llvm::Instruction *alloc_inst;
		heap_label(llvm::Instruction * ai){
			alloc_inst = ai;
			type = heap_t;
		}
		~heap_label(){
			if(alloc_inst) delete alloc_inst;
		}
		int label_cmp(label *rhs);
	};

	struct deref_label : label{
		label *lab;
		deref_label(label *l){
			lab = l;
			type = deref_t;
		}
		~deref_label(){
			if(lab) delete lab;
		}
		int label_cmp(label *rhs);
	};

	struct constraint_builder{
		std::vector<constraint*> constraints;
		std::vector<label*> labels;
		llvm::Module *module;
		constraint_builder(llvm::Module *mod){
			module = mod;
		}
		~constraint_builder(){
			if(module) delete module;
		}
		void for_each_function();
		void populate_labels(llvm::Instruction *inst);
		label *build_address_label(llvm::Value *v);
		label *build_value_label(llvm::Value *v);
		void parse_address_label(label *l, bool &deref, int &label_id);
		void parse_value_label(label *l, bool &deref, int &label_id);
		void dump_constraints(std::ostream &out);
		void dump_labels(std::ostream &out);
		void print_constraint(std::ostream &out, constraint *c);
		void print_label(std::ostream &out, label* l, bool verbose);
		bool is_mem(label *l);
		bool is_var_value(label *l);
		void insert_label(label *l);
		int get_id(label *l, bool create);
		label *get_label(int id);
	};
}
#endif
